package com.aizuda.encrypt.entity;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import com.aizuda.encrypt.config.EncryptProperties;
import com.aizuda.encrypt.enums.EncryptType;
import com.aizuda.encrypt.util.EncryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.SqlCommandType;

import java.util.Map;
import java.util.Optional;

/**
 * 加密规则
 *
 * @date 2022-02-28 10:24:06
 */
@Slf4j
public class EncryptRule {

    private final Map<String, TableRule> tables;
    private final String                 key;
    private final EncryptType            type;

    /**
     * 加密规则
     *
     * @param properties 配置
     * @author nn200433
     */
    public EncryptRule(EncryptProperties properties) {
        this.tables = properties.getTables();
        this.key    = properties.getEncryptKey();
        this.type   = properties.getEncryptType();
    }

    /**
     * 获取加密值
     *
     * @param tableName      表名
     * @param columnName     列名
     * @param originalValues 原始值
     * @param params         参数
     * @return {@link Object }
     * @author nn200433
     */
    public Object getEncryptedValues(String tableName, String columnName, final Object originalValues, EncryptedValuesParams params) {
        // 非 String 类型直接返回
        if (!originalValues.getClass().equals(String.class)) {
            return originalValues;
        }
        final ColumnRule columnRule = getColumnRule(tableName, columnName);
        if (null == columnRule) {
            return originalValues;
        }
        final Boolean encryptor = columnRule.getEncryptor();
        final Integer splitChar = columnRule.getSplitChar();
        if (encryptor) {
            // 进行加密处理
            final String         originalStr = Convert.toStr(originalValues);
            final int            originalLen = originalStr.length();
            final Boolean        isAddIndex  = params.getIsAddIndex();
            final SqlCommandType sqlType     = params.getSqlType();
            if (SqlCommandType.SELECT == sqlType && originalLen != splitChar) {
                // 由于可能需要精准匹配的情况（比如后端判断加密列数据是否存在），所以只进行提示输出
                log.warn("---> [加密规则] 建议查询条件 {}.{} 的数据需要等于{}！", tableName, columnName, splitChar);
            }
            return EncryptUtil.encrypt(type, key, originalStr, splitChar, isAddIndex);
        }
        return originalValues;
    }

    /**
     * 获取加密列规则
     *
     * @param tableName  表名
     * @param columnName 列名
     * @return {@link Optional<ColumnRule> }
     * @author nn200433
     */
    public Optional<ColumnRule> getColumnRuleOptional(String tableName, String columnName) {
        columnName = columnName.toLowerCase();
        final TableRule tableRule = tables.get(tableName);
        if (null == tableRule) {
            return Optional.empty();
        }
        final Map<String, ColumnRule> columnMap = tableRule.getColumns();
        if (MapUtil.isEmpty(columnMap) || !columnMap.containsKey(columnName)) {
            // 在规则配置中查不到时返回空
            return Optional.empty();
        }
        return Optional.of(columnMap.get(columnName));
    }

    /**
     * 是否包含列
     *
     * @param tableName  表名
     * @param columnName 列名
     * @return boolean
     * @author nn200433
     */
    public boolean isContainsColumn(String tableName, String columnName) {
        return getColumnRuleOptional(tableName, columnName).isPresent();
    }

    /**
     * 获取加密列规则
     *
     * @param tableName  表名
     * @param columnName 列名
     * @return {@link ColumnRule }
     * @author nn200433
     */
    private ColumnRule getColumnRule(String tableName, String columnName) {
        // 获取列规则
        final Optional<ColumnRule> columnRuleOptional = getColumnRuleOptional(tableName, columnName);
        return columnRuleOptional.isPresent() ? columnRuleOptional.get() : null;
    }

}
